package com.example.security.security;

import com.example.security.bean.User;
import com.example.security.dao.UserDao;
import com.example.security.util.CustomResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  private final UserDao userDao;

  public SecurityConfig(UserDao userDao) {
    this.userDao = userDao;
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    // auth.userDetailsService().passwordEncoder();
    auth.userDetailsService(
            username -> {
              User user = userDao.getUserByName(username);
              if (user == null) {
                throw new UsernameNotFoundException("没有这个用户:" + username);
              }
              // 通过自定义的User对象生成UserDetails对象并返回
              // 需要实例化UserDetails接口
              return new MyUserDetails(user);
            })
        .passwordEncoder(NoOpPasswordEncoder.getInstance());
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.httpBasic().disable();

    http.addFilter(new JwtLoginFilter(authenticationManager()))
        .addFilter(new JwtAuthenticationFilter(authenticationManager()));

    // 401, 未登录
    http.exceptionHandling()
        .authenticationEntryPoint(
            (request, response, authException) ->
                CustomResponseEntity.toJson(
                    response, HttpStatus.UNAUTHORIZED.value(), -1, "用户未登录"));

    // 403, 登录了权限不够
    http.exceptionHandling()
        .accessDeniedHandler(
            (request, response, accessDeniedException) ->
                CustomResponseEntity.toJson(response, HttpStatus.FORBIDDEN.value(), -2, "无权限访问"));

    // 以下这条语句是配置登录成功或是失败的处理,
    // 如果不配置这个, 那么在http.formLogin()的场景下会跳转到/路径或是/login路径
    //    http.formLogin().successHandler(genSuccessHandler()).failureHandler(genFailureHandler());

    // 注册接口所有人都可以访问
    http.authorizeRequests().antMatchers("/register", "/active/**").permitAll();

    // 访问/admin需要ADMIN的角色
    http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");

    // USER用户可以访问所有的页面
    http.authorizeRequests().anyRequest().hasRole("USER");

    // 实现了JWT之后就无需再使用session来保存用户的登录状态,
    // 因为之前的formLogin会将Authentication对象保存在Session中
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  }

  // formLogin配置下登录成功的处理
  // 不增加这个处理, 登录成功则会跳转到 / 路径
  AuthenticationSuccessHandler genSuccessHandler() {
    return (request, response, authentication) ->
        CustomResponseEntity.toJson(response, HttpStatus.OK.value(), 0, "登录成功");
  }

  // formLogin配置下登录失败的处理
  // 不增加这个处理, 登录失败则会跳转到 /login 路径
  AuthenticationFailureHandler genFailureHandler() {
    return (request, response, exception) ->
        CustomResponseEntity.toJson(response, HttpStatus.BAD_REQUEST.value(), -5, "用户名或密码不正确");
  }
}
